package com.plm.mqshequ.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;

/**
 * Shiro工具配置器
 * Authentication：身份认证/登录，验证用户是不是拥有相应的身份；
 * Authorization：授权，即权限验证，验证某个已认证的用户是否拥有某个权限；验证是否拥有角色、资源
 * Session Manager：会话管理，即用户登录后就是一次会话，在没有退出之前，它的所有信息都在会话中
 * Cryptography：加密
 * Web Support：Web 支持
 * Caching：缓存，比如用户登录后，其用户信息、拥有的角色/权限不必每次去查，这样可以提高效率；
 * Concurrency：Shiro支持多线程应用的并发验证，即如在一个线程中开启另一个线程，能把权限自动传播过去；
 * Remember Me：记住我，这个是非常常见的功能，即一次登录后，下次再来的话不用登录了
 * @author Limi
 * @create 2019-01-17 17:33
 */
@Configuration
public class ShiroConfig {

    /**
     * • 1、首先调用 Subject.login(token) 进行登录，其会自动委托给 SecurityManager
     * • 2、SecurityManager 负责真正的身份验证逻辑；它会委托给 Authenticator 进行身份验证；
     * • 3、Authenticator 才是真正的身份验证者，Shiro API 中核心的身份 认证入口点，此处可以自定义插入自己的实现；
     * • 4、Authenticator 可能会委托给相应的 AuthenticationStrategy 进 行多 Realm 身份验证，
     * 默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证；
     * • 5、Authenticator 会把相应的 token 传入 Realm，从 Realm 获取 身份验证信息，
     * 如果没有返回/抛出异常表示身份验证失败了。此处 可以配置多个Realm，将按照相应的顺序及策略进行访问
     */

    /**
     * Shiro拦截器
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //默认跳转到登陆页面
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        //登陆成功后的页面
        shiroFilterFactoryBean.setSuccessUrl("/");
        //未授权的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        /*
         * 访问过滤
         * anon（anonymous） 拦截器表示匿名访问（即不需要登 录即可访问）
         * authc（authentication）拦截器表示需要身份认证通过后 才能访问
         * logout 注销
         */
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/post/page/**", "anon");
        filterChainDefinitionMap.put("/post/index", "anon");
        filterChainDefinitionMap.put("/post/reply", "anon");
        filterChainDefinitionMap.put("/post/zan", "anon");
        filterChainDefinitionMap.put("/post/detail/**", "anon");
        filterChainDefinitionMap.put("/post/query/**", "anon");
        filterChainDefinitionMap.put("/authcode/calculate", "anon");
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/authEmail", "anon");
        filterChainDefinitionMap.put("/user/loginHandle", "anon");
        filterChainDefinitionMap.put("/user/reg", "anon");
        filterChainDefinitionMap.put("/user/regHandle", "anon");
        filterChainDefinitionMap.put("/user/forget", "anon");
        filterChainDefinitionMap.put("/user/forgetHandle", "anon");
        filterChainDefinitionMap.put("/user/jump/**", "anon");
        filterChainDefinitionMap.put("/user/uid/**", "anon");
        //filterChainDefinitionMap.put("/user/logout", "logout");//采用手动注销
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 核心的安全事务管理器
     */
    @Bean("securityManager")
    public SecurityManager securityManager () {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm());
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }


    /**
     * 身份认证Realm，此处的注入不可以缺少。否则会在shiroRealm中注入对象会报空指针
     */
    @Bean
    public ShiroRealm shiroRealm() {
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());// 设置加密
        return shiroRealm;
    }

    /**
     * 哈希密码比较器
     * 在ShiroRealm中作用参数使用，登陆时会比较用户输入的密码，跟数据库密码配合盐值salt解密后是否一致
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");//散列算法:这里使用md5算法;
        hashedCredentialsMatcher.setHashIterations(10000);//散列的次数，比如散列两次，相当于 md5( md5(""));
        return hashedCredentialsMatcher;
    }

    /**
     * 开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 自动创建代理
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * Shiro生命周期处理器
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * cookie管理对象
     */
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        // rememberMe cookie加密的密钥
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    /**
     * cookie对象
     */
    public SimpleCookie rememberMeCookie(){
        // 设置cookie名称，对应login.html页面的<input type="checkbox" name="rememberMe"/>
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        // 设置cookie的过期时间，单位为秒，这里为一小时
        cookie.setMaxAge(60*60);
        return cookie;
    }

}
